package com.neusoft.databus.agent.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Application;
import com.neusoft.bizcore.web.dto.result.ResultDTO;
import com.neusoft.bizcore.web.dto.result.ResultListDTO;
import com.neusoft.databus.agent.bean.PoolStatusBean;
import com.neusoft.databus.agent.pool.opc.OpcConnector;
import com.neusoft.databus.agent.pool.serial.SerialConnector;
import com.neusoft.databus.agent.pool.snmp.SnmpConnector;
import com.neusoft.databus.agent.pool.socket.SocketConnector;
import com.neusoft.databus.agent.pool.socket2.Socket2Connector;
import com.neusoft.databus.agent.service.SupportService;
import com.neusoft.databus.common.connection.OpcConnection;
import com.neusoft.databus.common.connection.SerialConnection;
import com.neusoft.databus.common.connection.SnmpConnection;
import com.neusoft.databus.common.connection.SocketConnection;
import com.neusoft.databus.common.constant.DatabusConstant;

/**
 * 服务支撑控制器<br>
 * 提供运维接口
 *
 * @author sunchf
 * @date 2018年12月28日 上午10:13:58
 */

@RestController
@RequestMapping(value = "/api/w/support")
public class SupportController {

    @Autowired
    private SupportService supportService;
    @Autowired
    private SocketConnector socketConnector;
    @Autowired
    private Socket2Connector socket2Connector;
    @Autowired
    private SnmpConnector snmpConnector;
    @Autowired
    private SerialConnector serialConnector;
    @Autowired
    private OpcConnector opcConnector;
    @Autowired
    private EurekaClient eurekaClient;
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/pools")
    public ResultListDTO<PoolStatusBean> pools() {
        final List<PoolStatusBean> beans = new ArrayList<>();

        final Application agentApp = this.eurekaClient.getApplication(DatabusConstant.MICRO_SERVICE_NAME_AGENT);
        final List<String> availableNodes = agentApp.getInstances().stream().map(it -> {
            return it.getIPAddr() + ":" + it.getPort();
        }).collect(Collectors.toList());

        availableNodes.forEach(url -> {
            ResponseEntity<ResultListDTO<PoolStatusBean>> response = null;
            try {

                final ParameterizedTypeReference<ResultListDTO<PoolStatusBean>> typeRef =
                        new ParameterizedTypeReference<ResultListDTO<PoolStatusBean>>() {
                        };
                response = this.restTemplate.exchange("http://" + url + "/api/w/support/pools/intenal", HttpMethod.GET,
                        null,
                        typeRef);
            } catch (final Exception e) {
                e.printStackTrace();
            }
            if (null != response) {
                response.getBody().getData().stream().map(it -> {
                    final PoolStatusBean bean = it;
                    bean.setNode(url);
                    return bean;
                }).forEach(beans::add);
            }
        });

        return ResultListDTO.success(beans);

    }

    @GetMapping("/pools/intenal")
    public ResultListDTO<PoolStatusBean> intenalPools() {
        final List<PoolStatusBean> beans = this.supportService.realtimePoolsStatus();
        return ResultListDTO.success(beans);
    }

    /**
     * 清空连接池。确保连接池内无等候线程；确保连接池内所有连接已经归还；
     *
     * @param node 节点地址
     * @param type snmp/socket/serial
     * @param key
     * @return
     */

    @DeleteMapping("/pools/{node}/{type}/{key}")
    public ResultDTO<Void> removePooledObjectInPoolsWithNode(@PathVariable final String node,
            @PathVariable final String type,
            @PathVariable final String key) {

        try {
            final ParameterizedTypeReference<ResultDTO<Void>> typeRef =
                    new ParameterizedTypeReference<ResultDTO<Void>>() {
                    };
            final String uri = "/api/w/support/pools/" + type + "/" + key;
            this.restTemplate.exchange("http://" + node + uri, HttpMethod.DELETE, null, typeRef);
        } catch (final Exception e) {
            e.printStackTrace();
        }

        return ResultDTO.success();
    }

    @SuppressWarnings("deprecation")
    @DeleteMapping("/pools/{type}/{key}")
    public ResultDTO<Void> removePooledObjectInPools(@PathVariable final String type, @PathVariable final String key) {

        switch (type) {
        case "socket":
        case "socket2":
        case "snmp":
            final String[] tuple = key.split(":");
            if ("socket".equalsIgnoreCase(type)) {
                final SocketConnection connection = new SocketConnection();
                connection.setIpAddress(tuple[0]);
                connection.setPort(Integer.valueOf(tuple[1]));
                this.socketConnector.removeAllConns(connection);
            } else if ("socket2".equalsIgnoreCase(type)) {
                final SocketConnection connection = new SocketConnection();
                connection.setIpAddress(tuple[0]);
                connection.setPort(Integer.valueOf(tuple[1]));
                this.socket2Connector.removeAllConns(connection);
            } else if ("snmp".equalsIgnoreCase(type)) {
                final SnmpConnection connection = new SnmpConnection();
                connection.setIpAddress(tuple[0]);
                connection.setPort(Integer.valueOf(tuple[1]));
                connection.setCommunity(tuple[2]);
                this.snmpConnector.removeAllConns(connection);
            }
            break;

        case "serial":
            final SerialConnection connection = new SerialConnection();
            connection.setComValue(key);
            this.serialConnector.removeAllConns(connection);
            break;
        case "opc":
            final OpcConnection opcConnection = new OpcConnection();
            opcConnection.setHost(key);
            this.opcConnector.removeAllConns(opcConnection);
            break;
        default:
            break;
        }

        return ResultDTO.success();
    }

}
